import { configs } from 'config'
import * as $ from 'jquery'
import { sha256 } from 'js-sha256'
const IMG_HI_RES_SUFFIX = '@2x'

declare const WXEnvironment: any

export namespace utils {
  export const KEYS = {
    ENTER: 13,
    BACKSPACE: 8
  }

  /**
   * 获得URL中”？“后面的参数 以键值对的形式放回
   * @param url string类型
   */
  export function getUrlArgs(
    url: string = window.location.href,
    isCamelize: boolean = false
  ): { [key: string]: string } | null {
    // remove url hash
    const [href] = url.split('#')
    const s = href.split('?')[1]
    if (!s) {
      return {}
    }
    let args: {
      [key: string]: string;
    } | null = {}
    for (const a of s.split('&')) {
      const part = a.split('=')
      let str: string = ''
      try {
        str = decodeURIComponent(part[1])
      } catch (error) {
        str = part[1]
      }
      args[part[0]] = str
    }
    if (isCamelize) {
      args = utils.camelize(args)
    }
    return args
  }

  /**
   * 生成URL链接
   * @param path URL中的路径
   * @param queryArgs URL中 “?” 后面的参数
   */
  export function genApiUrl(path: string, queryArgs: object | null): string {
    if (!queryArgs) {
      queryArgs = {}
    }
    const args: string[] = []
    for (const key in queryArgs as any) {
      const v = (queryArgs as any)[key]
      if (v !== null) {
        args.push(`${key}=${v}`)
      }
    }
    if (args.length === 0) {
      return path
    } else {
      return `${path}?${args.join('&')}`
    }
  }

  /**
   * 从当前页面的 url hash 中获取所有参数，以键值对形式返回
   */
  export function getHashArgs(): { [key: string]: string } | null {
    const hash: string = window.location.hash
    if (!hash) {
      return {}
    } else {
      return utils.getUrlArgs(`?${hash.slice(1)}`)
    }
  }

  /**
   * 向当前页面的 url hash 中添加指定参数，会按照 url query 的方式组织
   * @param key 参数名 string
   * @param value 参数值 string | null  为 null 的时候是将哈希 key 和 对应的值  清除
   */
  export function setHashArg(key: string, value: string | null) {
    const args = utils.getHashArgs()
    if (value === null) {
      if (args![key]) {
        delete args![key]
      } else {
        throw new Error(`invalid ${key}`)
      }
    } else {
      args![key] = value
    }
    window.location.hash = utils.genApiUrl('', args!).slice(1)
  }

  /**
   * 将key字符串转换成下划线方式命名 (如 "some_name") 的字符串
   * @param key 对象字符串
   * @param ignoreFirst 是否忽略第一个大写字母，如果忽略，会将其当成小写字母处理
   */
  export function underlizeKey(
    key: string,
    ignoreFirst: boolean = false
  ): string {
    const out: string[] = []
    let i: number = 0
    const lowerCasedStr: string = key.toString().toLowerCase()
    while (i < key.length) {
      if (key[i] !== lowerCasedStr[i]) {
        if (!ignoreFirst || i !== 0) {
          out.push('_')
          out.push(lowerCasedStr[i])
          i++
          continue
        }
      }
      out.push(key[i].toLocaleLowerCase())
      i++
    }
    return out.join('')
  }

  /**
   * 将对象键值对中的 key 转换为按照下划线方式命名的 key
   * @param obj 需要转换的对象
   */
  export function underlize(obj: object | null): object | null {
    if (obj === null || obj === undefined) {
      return null
    } else if (obj instanceof Array) {
      return obj.map((item) => {
        return utils.underlize(item)
      })
    } else if (typeof obj === 'object') {
      const out: any = {}
      for (const key in obj as any) {
        const v = (obj as any)[key]
        out[utils.underlizeKey(key)] = utils.underlize(v)
      }
      return out
    } else {
      return obj
    }
  }

  /**
   * 将key字符串转换成中划线方式命名 (如 "some-name") 的字符串
   * @param key 对象字符串
   * @param ignoreFirst 是否忽略第一个大写字母，如果忽略，会将其当成小写字母处理
   */
  export function middlelizeKey(
    key: string,
    ignoreFirst: boolean = false
  ): string {
    const out: string[] = []
    let i: number = 0
    const lowerCasedStr = key.toString().toLowerCase()
    while (i < key.length) {
      if (key[i] !== lowerCasedStr[i]) {
        if (!ignoreFirst || i !== 0) {
          out.push('-')
          out.push(lowerCasedStr[i])
          i++
          continue
        }
      }
      out.push(key[i].toLocaleLowerCase())
      i++
    }
    return out.join('')
  }

  /**
   * 将对象键值对中的 key 转换为按照下划线方式命名的 key
   * @param obj 需要转换的对象
   */
  export function middlelize(obj: object): object | null {
    if (obj === null || obj === undefined) {
      return null
    } else if (obj instanceof Array) {
      return obj.map((item) => {
        return utils.underlize(item)
      })
    } else if (typeof obj === 'object') {
      const out: any = {}
      for (const key in obj as any) {
        const v = (obj as any)[key]
        out[utils.middlelizeKey(key)] = utils.middlelize(v)
      }
      return out
    } else {
      return obj
    }
  }

  /**
   * 将key字符串转换成驼峰方式命名（如 "someName"） 的字符串
   * @param key string类型
   * @param separators key分隔符 "-"中划线/"_"下划线
   */
  export function camelizeKey(
    key: string,
    separators: string[] = ['-', '_']
  ): string {
    const out: any = []
    let i: number = 0
    const separatorsSet = new Set(separators)
    while (i < key.length) {
      if (separatorsSet.has(key[i])) {
        out.push(key[i + 1].toUpperCase())
        i++
      } else {
        out.push(key[i])
      }
      i++
    }
    return out.join('')
  }

  /**
   * 将对象键值对中的 key 转换为按照驼峰方式命名的 key
   * @param obj
   */
  export function camelize(obj: object): { [key: string]: any } | null {
    if (obj === null || obj === undefined) {
      return null
    } else if (obj instanceof Array) {
      return obj.map((item) => {
        return utils.camelize(item)
      })
    } else if (typeof obj === 'object') {
      const out: any = {}
      for (const key in obj as any) {
        const v = (obj as any)[key]
        out[utils.camelizeKey(key)] = utils.camelize(v)
      }
      return out
    } else {
      return obj
    }
  }

  /**
   * 将一个时间对象和时间戳转换成对应的时间格式
   * @param time 时间对象和时间戳
   * @param format 时间格式, 参数：y年m月d日h点i分s秒c毫秒，w=weekDay，YMD分别是年月日的简写，
   */
  export function parseTime(
    time: Date | number,
    format = 'Y年M月D日h点i分'
  ): string {
    // keys = "Y","M","D","d","h","m","s","c"
    if (!time) { return '' }
    if (typeof time === 'number') {
      time = utils.normalizeTime(time)
    }
    const arr: any = format.split('')
    const date = new Date(time as any)
    for (let index = 0; index < arr.length; index++) {
      const value = arr[index]
      switch (value) {
        case 'Y':
          arr[index] = date.getFullYear() - 2000
          break
        case 'M':
          arr[index] = date.getMonth() + 1
          break
        case 'D':
          arr[index] = date.getDate()
          break
        case 'y':
          arr[index] = date.getFullYear()
          break
        case 'm':
          const m: number = date.getMonth() + 1
          arr[index] = m < 10 ? `0${m}` : m.toString()
          break
        case 'd':
          const d: number = date.getDate()
          arr[index] = d < 10 ? `0${d}` : d.toString()
          break
        case 'w':
          const w: number = date.getDay()
          switch (w) {
            case 1:
              arr[index] = '一'
              break
            case 2:
              arr[index] = '二'
              break
            case 3:
              arr[index] = '三'
              break
            case 4:
              arr[index] = '四'
              break
            case 5:
              arr[index] = '五'
              break
            case 6:
              arr[index] = '六'
              break
            case 0:
              arr[index] = '日'
              break
          }
          break
        case 'h':
          arr[index] = date.getHours().toString()
          while (arr[index].length < 2) {
            arr[index] = '0' + arr[index]
          }
          break
        case 'i':
          arr[index] = date.getMinutes().toString()
          while (arr[index].length < 2) {
            arr[index] = '0' + arr[index]
          }
          break
        case 's':
          arr[index] = date.getSeconds().toString()
          while (arr[index].length < 2) {
            arr[index] = '0' + arr[index]
          }
          break
        case 'c':
          arr[index] = date.getMilliseconds().toString()
          while (arr[index].length < 3) {
            arr[index] = '0' + arr[index]
          }
          break
      }
    }
    return arr.join('')
  }

  /**
   * 将指定时间戳转换成类似 ”5分钟前“ 这样的形式
   * @param time unix时间戳或Date对象，如果传入时间戳可以m秒为单位，或者毫秒为单位
   * @param detailed 输出简略格式或者详细格式，默认简略
   */
  export function timeFromNow(
    time: Date | number,
    detailed: boolean = false
  ): string {
    let dateTime: Date
    if (time instanceof Date) {
      dateTime = time
    } else {
      dateTime = new Date(utils.normalizeTime(time))
    }
    const now = new Date().getTime()
    const d = now - dateTime.getTime()
    const thatday = utils.parseTime(dateTime, 'YMD')
    const today = utils.parseTime(now, 'YMD')
    const yesterday = utils.parseTime(now - 24 * 3600 * 1000, 'YMD')
    if (dateTime.getFullYear() !== new Date().getFullYear()) {
      // 不是今年
      if (detailed) {
        return utils.parseTime(dateTime, 'Y-M-D h:i')
      } else {
        return utils.parseTime(dateTime, 'M月D日')
      }
    }
    if (thatday !== today && thatday !== yesterday) {
      // 昨天以前
      if (detailed) {
        return utils.parseTime(dateTime, 'Y-M-D h:i')
      } else {
        return utils.parseTime(dateTime, 'M月D日')
      }
    }
    if (thatday === yesterday) {
      return `昨天${utils.parseTime(dateTime, 'h:i')}`
    }
    if (d > 3600 * 1000) {
      return `${Math.floor(d / 1000 / 60 / 60)}小时前`
    }
    if (d > 60 * 1000) {
      return `${Math.floor(d / 1000 / 60)}分钟前`
    }
    return '刚刚'
  }

  /**
   * 用于统一毫秒单位时间戳和秒单位时间戳
   * @param time 时间戳
   * @param toSecond 最终计算出来是否以秒为单位
   */
  export function normalizeTime(
    time: number,
    toSecond: boolean = false
  ): number {
    time = time * 1
    if (toSecond) {
      if (time > new Date('3000-01-01').getTime() / 1000) {
        time = time / 1000
      }
    } else {
      if (time < new Date('1971-01-01').getTime()) {
        time = time * 1000
      }
    }
    return time
  }

  /**
   * 生成随机字符串
   */
  export function genSessionId(): string {
    return (
      Date.now().toString(36) +
      parseInt(Math.random() * 100000 + '').toString(36)
    )
  }

  /**
   * base64格式转二进制对象
   * @param base64 base64格式的string
   * @param contentType 内容
   * @param sliceSize 截取大小
   */
  export function base64ToBlob(
    base64: string,
    contentType: string = '',
    sliceSize: number = 512
  ): Blob {
    const byteCharacters = atob(base64)
    const byteArrays = []
    let offset = 0
    while (offset < byteCharacters.length) {
      const slice = byteCharacters.slice(offset, offset + sliceSize)
      const byteNumbers = new Array(slice.length)
      let i = 0
      while (i < slice.length) {
        byteNumbers[i] = slice.charCodeAt(i)
        i++
      }
      byteArrays.push(new Uint8Array(byteNumbers))
      offset += sliceSize
    }
    const blob = new Blob(byteArrays, {
      type: contentType
    })
    return blob
  }

  /**
   * 获得 chrome 内核版本
   */
  export function getChromeVersion(): number {
    const res = /Chrome\/([0-9.]+)/.exec(navigator.userAgent)![1]
    return parseInt(res)
  }

  export function getExtensionId(): string | null {
    return $(document.body).attr('mpa-extension-id') || null
  }

  /**
   * 判断入口是pc端还是移动端
   * 移动端返回true 否则返回false
   */
  export function isMobileUserAgent(): boolean {
    const userAgentInfo = navigator.userAgent
    return !!userAgentInfo.match(/AppleWebKit.*Mobile.*/)
  }

  /**
   * 或操作，类似操作符 “||” ，但是只有 null 和 undefin 的时候会命中， 0 或 false 时不会
   */
  export function or<T>(value1: T, value2: T): T {
    return value1 !== undefined && value1 !== null ? value1 : value2
  }

  /**
   * 是否为高分辨率
   */
  export function isHighDensity(): boolean {
    return window.devicePixelRatio > 1
  }

  /**
   * 检查是否需要更新
   * @param currentVersion 当前版本
   * @param remoteVersion  最新版本
   */
  export function checkIfNeedUpdate(
    currentVersion: string,
    remoteVersion: string
  ): boolean {
    const [a, b, c] = currentVersion.split('.')
    const [a1, b1, c1] = remoteVersion.split('.')
    const v1 = parseInt(a) * 1000000 + parseInt(b) * 1000 + parseInt(c) * 1
    const v2 = parseInt(a1) * 1000000 + parseInt(b1) * 1000 + parseInt(c1) * 1
    if (v2 > v1) {
      return true
    } else {
      return false
    }
  }

  /**
   * 找出给定 html 中所有的 inline script，可以指定过滤用正则表达式
   * @param html 网页html
   * @param match 正则表达式，如果指定，则只会返回命中的script
   */
  export function getInlineScriptFromHtml(
    html: string,
    match?: RegExp
  ): string[] {
    const wrap = document.createElement('section')
    wrap.innerHTML = utils.removeRefsFromHtml(html, false)
    const res: string[] = []
    for (
      let arr = wrap.querySelectorAll('script'), i = 0;
      i < arr.length;
      i++
    ) {
      const script = arr[i]
      if (script.src || (script.type && script.type !== 'text/javascript')) {
        // 跳过非 js 类型 script，和非 inline 的 script
        continue
      }
      if (match) {
        if (match.test(script.innerHTML)) {
          res.push(script.innerHTML)
        }
      } else {
        res.push(script.innerHTML)
      }
    }
    return res
  }

  /**
   * 克隆给定对象
   * @param target 源对象
   * @param deepClone 是否进行深拷贝，如果为 false，只进行一层拷贝，深层都为引用
   */
  export function clone<T>(target: T, deepClone = false): T {
    if (target instanceof Array) {
      const res = []
      for (const item of target) {
        if (deepClone) {
          res.push(utils.clone(item), deepClone)
        } else {
          res.push(item)
        }
      }
      return res as any
    } else if (typeof target === 'object') {
      const res = {} as any
      for (const key in target) {
        if (deepClone) {
          res[key] = utils.clone(target[key], deepClone)
        } else {
          res[key] = target[key]
        }
      }
      return res
    } else {
      return target
    }
  }

  /**
   * 将键值对转换成列表
   * @param map 键值对map
   */
  export function mapToList<T>(map: { [key: string]: T }): T[] {
    const res: T[] = []
    for (const key in map) {
      res.push(map[key])
    }
    return res
  }

  /**
   * 将列表转换成键值对
   * @param list 列表
   * @param hashKey 哈希字段，会用作 map 的 key
   */
  export function listToMap<T, K extends keyof T>(
    list: T[],
    hashKey: K
  ): { [key: string]: T } {
    const map: { [key: string]: T } = {}
    for (const item of list) {
      const hash: string = (item as any)[hashKey]
      if (!hash) {
        console.error(`Can't find key ${hash} in`, item)
      }
      map[hash] = item
    }
    return map
  }

  /**
   * 向给定 url 后添加 query 参数
   */
  export function addUrlArgs(
    url: string,
    args: { [key: string]: string | number }
  ): string {
    const res = url.split('#')
    let baseUrl = res[0]
    const hash = res[1]
    const parts: string[] = []
    for (const k in args) {
      const v = args[k]
      if (v !== undefined && v !== null) {
        parts.push(k + '=' + encodeURIComponent(v.toString()))
      }
    }
    if (baseUrl.indexOf('?') > -1) {
      baseUrl += '&' + parts.join('&')
    } else {
      baseUrl += '?' + parts.join('&')
    }
    if (hash) {
      return baseUrl + '#' + hash
    } else {
      return baseUrl
    }
  }

  /**
   * 删除 html string 中的 script 标签，img 和 link
   * @param html html 字符串
   * @param removeScripts 是否去除 script 标签
   */
  export function removeRefsFromHtml(
    html: string,
    removeScripts: boolean = false
  ): string {
    html = html.replace(/<img/g, '<noimg').replace(/<link /g, '<nolink ')
    if (removeScripts) {
      html = html.replace(/<(\/?)script/g, '<$1noscript')
    }
    return html
  }

  /**
   * 构建一个元素容器，并将指定 html 内容放置进去，会调用 removeRefsFromHtml 来去除 html 中的外部引用和 scripts
   * @param html html 字符串
   */
  export function parseHtmlForData(html: string): HTMLElement {
    const wrap = document.createElement('div')
    html = utils.removeRefsFromHtml(html, true)
    html = html.replace(/<(\/?)body/g, '<$1bodydiv')
    html = html.replace(/<(\/?)head/g, '<$1headdiv')
    wrap.innerHTML = html
    return wrap
  }

  /**
   * 生成距离现在给定天数时的时间，格式可以通过 format 参数定制，规则与 parseTime 相同
   * @param offset 时间偏移量
   * @param format 时间格式
   */
  export function getTimeByOffset(offset: number, format = 'y-m-d'): string {
    const t = Date.now() + offset * (24 * 3600 * 1000)
    return utils.parseTime(t, format)
  }

  type MaxRetryHandler = (error: any) => boolean
  interface RetryUntilResolveRes<T> {
    handleMaxRetry: (handler: MaxRetryHandler) => RetryUntilResolveRes<T>
    toPromise: () => Promise<T>
  }

  export function retryUntilResolve<T>(
    handler: () => Promise<T>,
    retryLimit: number
  ): RetryUntilResolveRes<T> {
    let retryCount = 0
    let maxRetryHandler: MaxRetryHandler
    let p = new Promise<T>(function(resolve, reject) {
      function iter() {
        console.log(`call iter, ${retryCount}/${retryLimit}`)
        p = handler()
        if (!(p instanceof Promise)) {
          console.error(
            'utils.retryUntilResolve only accept handler that return Promise instance'
          )
          reject()
        }
        p.then(function(data) {
          resolve(data)
        })
        p.catch(function(e) {
          if (e === true) {
            // 上级handler使用reject(true)可以阻止默认重试行为
            return reject({ userAbort: true })
          }
          if (retryCount < retryLimit) {
            retryCount += 1
            return iter()
          }
          // TODO consider support async handler
          if (maxRetryHandler && maxRetryHandler(e) === true) {
            // 如果 maxRetryHandler 返回 true，则重新尝试
            retryCount += 0
            return iter()
          }
          reject({ userAbort: false })
        })
      }
      iter()
    })
    const res: RetryUntilResolveRes<T> = {
      handleMaxRetry(
        handlerFn: MaxRetryHandler
      ): RetryUntilResolveRes<T> {
        maxRetryHandler = handlerFn
        return res
      },
      toPromise(): Promise<T> {
        return p
      }
    }
    return res
  }

  export function forEachElement<T extends Node>(
    list: NodeListOf<T>,
    handler: (item: T, idx: number) => void
  ) {
    for (let arr = list, i = 0; i < arr.length; i++) {
      const el = arr[i]
      handler(el, i)
    }
  }

  export function unicodeToChar(text: string) {
    return text.replace(/\\u[\dA-F]{4}/gi, function(match) {
      return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16))
    })
  }

  export function addImageHIResSuffix(src: string): string {
    const srcParts = src.split('.')
    const ext = srcParts[srcParts.length - 1]
    if (ext === 'png' || ext === 'jpg' || ext === 'gif') {
      srcParts[srcParts.length - 2] += IMG_HI_RES_SUFFIX
    }
    return srcParts.join('.')
  }

  export function decodeHtmlEntities(str: string): string {
    return $('<textarea/>')
      .html(str)
      .text()
  }

  export function parseCurrency(fenPrice: number, detailed = false): string {
    const yuanPrice = fenPrice / 100
    return utils.numberToFixed(yuanPrice, 2, detailed)
  }

  export function numberToFixed(
    num: number,
    fixedLen: number,
    forceFiexed = false
  ): string {
    if (isNaN(num)) {
      return 'NaN'
    }
    const s = num.toString()
    const [i, f = ''] = s.split('.')
    let res = `${i}.${f.slice(0, fixedLen)}`
    if (f.length < fixedLen && forceFiexed) {
      for (let j = 1; j <= fixedLen - f.length; j++) {
        res += '0'
      }
    }
    if (res[res.length - 1] === '.') {
      res = res.slice(0, -1)
    }
    return res
  }

  export function random(a: number, b: number, returnInt = false) {
    let d = Math.random() * (b - a)
    if (returnInt) {
      d = Math.round(d)
    }
    return a + d
  }

  /**
   * 当tab处于后台时，如果触发了location.reload()，在插件的contentScript运行的时候 document.ready 就已经是 loaded 或者 interactive了
   * 目前原因不明，但是加入一个检查以确保 callback 可以被执行
   * @param callback
   */
  export function onDOMReady(callback: () => void) {
    // console.log(document.readyState)
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', callback)
    } else {
      // interactive, loaded or complete
      callback()
    }
  }

  /** 将url中的 http: 换成 https: */
  export function secureUrl(url: string) {
    return url.replace(/^http:/, 'https:')
  }

  /**
   * 计算时间差，放回相差多少年，多少月，多少天，多少小时，多少分钟
   * @param start 开始时间
   * @param end 结束时间
   */
  export function formatSpecificOffsetTime(start: Date | number,
                                           end: Date | number)
    : { year: number, month: number, days: number, hours: number, minutes: number } {
    const yearLevelValue = 365
    // 一个月按 30 天算
    const monthLevelValue = 30
    const dayLevelValue = 24 * 60 * 60 * 1000
    const hourLevelValue = 60 * 60 * 1000
    const minuteLevelValue = 60 * 1000
    // const secondLevelValue = 1000;
    let datetimeStart: Date
    let datetimeEnd: Date
    if (start instanceof Date) {
      datetimeStart = start
    } else {
      datetimeStart = new Date(utils.normalizeTime(start))
    }
    if (end instanceof Date) {
      datetimeEnd = end
    } else {
      datetimeEnd = new Date(utils.normalizeTime(end))
    }
    const period = datetimeEnd.getTime() - datetimeStart.getTime()
    if (period <= 0) {
      return { year: 0, days: 0, hours: 0, minutes: 0, month: 0 }
    }
    // 计算出相差天数
    const totalDays = Math.floor(period / dayLevelValue)
    // 计算出小时数
    const hours = Math.floor(period % dayLevelValue / hourLevelValue)
    const minutes = Math.floor(period % dayLevelValue % hourLevelValue / (minuteLevelValue))
    const year = totalDays >= yearLevelValue ? Math.floor(totalDays / yearLevelValue) : 0
    const month = totalDays >= yearLevelValue
      ? Math.floor((totalDays - year * yearLevelValue) / monthLevelValue)
      : totalDays >= monthLevelValue ? Math.floor(totalDays / monthLevelValue) : 0
    const days = totalDays - month * 30 - year * yearLevelValue
    return { year, month, days, hours, minutes }
  }

  /**
   * 获取当前月份的天数
   * @param year number
   */
  export function getDays(year: number): number {
    const date = new Date()
    const mouth = date.getMonth() + 1// 获取当前月份
    let days// 定义当月的天数；
    if (mouth === 2) {// 当月份为二月时，根据闰年还是非闰年判断天数
      days = year % 4 === 0 ? 29 : 28
    } else if (
      mouth === 1 || mouth === 3 || mouth === 5 || mouth === 7 || mouth === 8 || mouth === 10 || mouth === 12) {
      // 月份为：1,3,5,7,8,10,12 时，为大月.则天数为31；
      days = 31
    } else {
      // 其他月份，天数为：30.
      days = 30
    }
    return days
  }

  export function inBrowser(): boolean {
    return typeof window !== 'undefined'
  }

  // Browser environment sniffing
  export function getUserAgent(): 'IE' | 'IE9' | 'isEdge' | 'android' | 'IOS' | 'chrome' {
    const UA = inBrowser && window.navigator.userAgent.toLowerCase()
    const isIE = UA && /msie|trident/.test(UA)
    const inWeex = typeof (WXEnvironment as any) !== 'undefined' && !!(WXEnvironment as any).platform
    const weexPlatform = inWeex && (WXEnvironment as any).platform.toLowerCase()
    if (isIE) {
      return 'IE'
    }
    const isIE9 = UA && UA.indexOf('msie 9.0') > 0
    if (isIE9) {
      return 'IE9'
    }
    const isEdge = UA && UA.indexOf('edge/') > 0
    if (isEdge) {
      return 'isEdge'
    }
    const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android')
    if (isAndroid) {
      return 'android'
    }
    const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios')
    if (isIOS) {
      return 'IOS'
    }
    const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge
    if (isChrome) {
      return 'chrome'
    }
    return 'chrome'
  }

  /**
   * Check if val is a valid array index.
   */
  export function isValidArrayIndex(val: any): boolean {
    const n = parseFloat(String(val))
    return n >= 0 && Math.floor(n) === n && isFinite(val)
  }

  const _toString = Object.prototype.toString
  /**
   * Strict object type check. Only returns true
   * for plain JavaScript objects.
   */
  export function isPlainObject(obj: any): boolean {
    return _toString.call(obj) === '[object Object]'
  }

  export function isRegExp(v: any): boolean {
    return _toString.call(v) === '[object RegExp]'
  }

  export function toNumber(val: string): number | string {
    const n = parseFloat(val)
    return isNaN(n) ? val : n
  }

  /**
   * Remove an item from an array.
   */
  export function removeItem(arr: any[], item: any): any[] | void {
    if (arr.length) {
      const index = arr.indexOf(item)
      if (index > -1) {
        return arr.splice(index, 1)
      }
    }
  }

  /**
   * Check whether an object has the property.
   */
  const hasOwnProperty = Object.prototype.hasOwnProperty
  export function hasOwn(obj: object | any[], key: string): boolean {
    return hasOwnProperty.call(obj, key)
  }

  export function toArray(list: any, start?: number): any[] {
    start = start || 0
    let i = list.length - start
    const ret: any[] = new Array(i)
    // 倒序遍历
    while (i--) {
      ret[i] = list[i + start]
    }
    return ret
  }

  /**
   * Ensure a function is called only once.
   */
  export function onceCallFn(fn: Function): Function {
    let called = false
    return function() {
      if (!called) {
        called = true
        fn.apply(window, arguments)
      }
    }
  }

  /**
   * Query an element selector if it's not an element already.
   */
  export function queryElement(el: string | Element): Element {
    if (typeof el === 'string') {
      const selected = document.querySelector(el)
      if (!selected) {
        console.warn(
          'Cannot find element: ' + el
        )
        return document.createElement('div')
      }
      return selected
    } else {
      return el
    }
  }

  /**
   * 合并两个对象
   * @param first 第一个对象
   * @param second  第二个对象
   */
  export function mergeObject<T extends object, U extends object>(first: T, second: U): T & U {
    const result = {} as T & U
    for (const id in first) {
      (result as T)[id] = first[id]
    }
    for (const id in second) {
      if (!result.hasOwnProperty(id)) {
        (result as U)[id] = second[id]
      }
    }

    return result
  }

  /**
   * 将数组转换为 元组类型
   * @param v
   */
  export function tuple<U extends any[]>(...v: U[]) {
    return v
  }

  /**
   * 保持对象字面量的声明
   * 避免 eg: const b = {a : "xxx"} => b: {a: string}, 而保持字面量的类型约束， {a: "xxx"}
   * @param v
   */
  export function raw<T extends string | number | symbol>(v: T) {
    return v
  }

  export function encrypt(password: string): string {
    const slot = 'sfjlasjdflsajflsajfksajlkdfsjl'
    return sha256.create().update(`${slot}#${password}`).hex()
  }

  export function convertToAntdFile(name: string, urls: string[]): UploadFile[] {
    const list: UploadFile[] = []
    for (const u of urls) {
      const uid = u.split('.')[0]
      const item: UploadFile = {
        uid,
        name,
        percent: 100,
        status: 'done',
        thumbUrl: `${configs.apiHost}${u}`,
        originUrl: u
      }

      list.push(item)

    }
    return list
  }
}

if (window) {
  (window as any).utils = utils
}
export default utils
